package com.dj.ms.event.support;

import com.alibaba.fastjson.JSON;
import com.dj.common.lang.utils.DateUtil;
import com.dj.ms.configure.EventBusConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Objects;

/**
 * <br>
 * <p>事件重试任务--定时计划</p>
 *
 * <br>
 *
 * @author ZJG
 * @version 1.0
 * @date 2021/7/26 下午7:08
 */
@Slf4j
@Component
@ConditionalOnExpression("${com.dj.ms.event.retry-enabled:false}")
@EnableAsync
@EnableScheduling
public class EventRetryTaskScheduled {

    @Autowired
    private EventBusConfiguration eventBusConfiguration;

    @Autowired
    private ApplicationContext applicationContext;

    @Scheduled(fixedRate = 10*1000L)
    public void retryEvent() {
        if (log.isTraceEnabled()) {
            log.trace("EventRetryTaskScheduled task starting. time::{}", DateUtil.getStringDate());
        }
        List<EventRetryTaskRepository> repositories = EventRetryTaskDataManager.getAllRepository();
        if (CollectionUtils.isEmpty(repositories)) {
            return;
        }
        repositories.forEach(repository -> {
            List<EventRetryTask> eventRetryTasks = repository.findByTimesLessThan(eventBusConfiguration.getRetryTimes().intValue());
            eventRetryTasks.forEach(eventRetryTask -> {
                try {
                    retry(eventRetryTask);
                    repository.delete(eventRetryTask);
                    log.info("event retry success! task:{}", JSON.toJSONString(eventRetryTask));
                } catch (Throwable throwable) {
                    eventRetryTask.setTimes(eventRetryTask.getTimes() + 1);
                    repository.save(eventRetryTask);
                    log.error("event retry error:{}", JSON.toJSONString(eventRetryTask), throwable);
                }
            });
        });
    }

    private void retry(EventRetryTask retryTask) throws Throwable {
        Class<?> clazz = Class.forName(retryTask.getClassName());
        Object object = applicationContext.getBean(clazz);
        Method method = clazz.getMethod(retryTask.getMethod(), Class.forName(retryTask.getParameterClassName()));
        method.invoke(object, retryTask.getEvent());
    }

    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(5);
        return taskScheduler;
    }

}
